// 守护进程化代码
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include "log.hpp"

extern Log lg;

// 每次登陆都会产生一个新会话，每个会话有自己的sid，一般sid就是bash的pid
// 每个会话只允许有一个前台任务，但可以有多个后台任务
// 任务（进程组）可以由多个进程构成
// 区分一个进程是前台进程还是后台进程是通过谁拥有标准输入来判断的
// 前台进程中止，bash将会代替前台进程

// 运行任务后+ & ：以后台方式启动
// fg：将任务切换到前台
// ^Z: 中止前台进程，切换至后台
// bg: 将中止的任务重新启动

// 守护进程：不受用户登录和退出影响的进程，也就是说是持续提供服务的进程，只要不关机，进程就一直存在
// 如何变成守护进程： 将当前进程变成一个独立的会话即可
//

const std::string null_file = "/dev/null";

void deamon(const std::string work_dir = "")
{
    // 1. 守护进程不能随意终止，所以需要忽略一些信号
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);

    // 2. 使用setsid将当前进程切换成一个独立的session
    // setsid 禁止进程组的组长直接变成会话，需要先托孤给OS
    if (fork() > 0)
        exit(0);
    if (setsid() == -1)
    {
        lg(Fatal, "set deamon error, errno:%d, errstr:%d", errno, strerror(errno));
        exit(1);
    }

    // 3. 更改工作目录
    if (work_dir.size())
        chdir(work_dir.c_str());

    // 4. 将调试的输入输出数据全部都重定向到“垃圾堆”中
    int fd = open(null_file.c_str(), O_RDWR);
    if (fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 2);
        dup2(fd, 1);
        close(fd);
    }
    else
    {
        lg(Fatal, "open %s error, errno:%d, errstr:%d", null_file.c_str(), errno, strerror(errno));
        exit(1);
    }
}